#!/bin/sh

#najde v souboru v maker.deps includy a zavola na ne addtodeps
searchforincludes() {
    j=1
    while [ "$j" -le `wc -l maker.deps | sed "s/ .*\$//"` ]; do
        dep=`cat maker.deps | sed -n "${j}p"`
        if [ ! -e "$dep" ]; then
            j=`expr $j + 1`
            continue
        fi
        cat "$dep" | sed -n '/^#include/{s/#include//;p}' | tr -d " \r\"<>" | sed "s/\.\///g" | addtodeps
        j=`expr $j + 1`
    done
}

#prida soubor na stdin do maker.includes, jestli uz tam neni
addtodeps() {
    while read file2; do
        cat maker.deps | grep "$file2" - > /dev/null;
        if [ $? != 0 ] && [ -e $file2 ]; then
            echo $file2 >> maker.deps
        fi
    done
}

#hleda main funkci v souboru jehoz jmeno je na vstupu
#skonci uspesne, pokud ji najde
searchformain() {
    read file3
    grep "int main[( ]" $file3 > /dev/null
    if [ $? -eq 0 ]; then
        exit 0
    fi
    exit 1
}

#prida objekt na stdin k maker.objects, pokud tam jeste neni
addtoobjects() {
    read object;
    if [ "$object" = "" ]; then
        exit 1
    fi
    grep $object maker.objects > /dev/null
    if [ $? -ne 0 ]; then
        echo $object >> maker.objects
    fi
}

#hleda nedefinovane reference v souborech v maker.objects a k nim
#patricne definice v jinych souborech a prida je do maker.objects
#takto porad dokola, dokud se meni soubor maker.undefined
searchforreferences() {
    l=1
    while [ "$l" -le `wc -l maker.objects | sed "s/ .*\$//"` ]; do
        object=`cat maker.objects | sed -n "${l}p"`
        if [ "$object" = "" ]; then
            l=`expr $l + 1`
            continue
        fi
        nm -o $object 2> /dev/null | tr -s " " " " | awk "(\$2 == \"U\") {print \$3}" > maker.undefined
        k=1
        while [ "$k" -le `wc -l maker.undefined | sed "s/ .*\$//"` ]; do
            #search for the undefined reference in all object files
            #and add those that define them to maker.objects
            #if they are not yet there
            symbol=`cat maker.undefined | sed -n "${k}p"`
            if [ "$symbol" != "" ]; then
                grep " $symbol" defined.symbols | awk "// {print \$1; exit}" | addtoobjects
            fi
            k=`expr $k + 1`
        done
        l=`expr $l + 1`
    done
}

trap 'rmdir ./maker.lock 2> /dev/null; rm -f ./maker.* defined.symbols 2> /dev/null; exit 1' TERM INT EXIT

mkdir maker.lock 2> /dev/null ;
if [ $? != 0 ]; then
    echo "maker is already running"
    echo "If you are sure it isn't, remove directory ./maker.lock"
    exit 1
fi

generate=0;

if [ $# -eq 0 ]; then
    if [ -e ./makerfile ]; then
        linkeropts=`head -n 2 ./makerfile | tail -n 1`
        cflags=`head -n 3 ./makerfile | tail -n 1`        
        mainname=`head -n 4 ./makerfile | tail -n 1`
        generate=1
    else
        echo "Maker"
        echo "By Czestmyr"
        echo
        echo "synopsis:"
        echo "maker.sh -g [-l \"libs\"] [-c \"compiler opts\"] [-m \"main name\"]"
        exit 0
    fi
fi

#argument parsing
while [ $# -ge 1 ]; do
    echo $1 | grep "^-[^-]" - > /dev/null
    if [ $? -eq 0 ]; then
        i=2
        length=`expr length $1`
        while [ $i -le $length ]; do
            option=`expr substr $1 $i 1`
            case $option in
            c )
                if [ $i -ne $length ] || [ $# -eq 1 ]; then
                    echo "Argument -c has no parameters after it"
                else
                    cflags="$2"
                    i=`expr length $1 + 1`
                    shift 1
                fi;;
            m )
                if [ $i -ne $length ] || [ $# -eq 1 ]; then
                    echo "Argument -m has no parameters after it"
                else
                    mainname="$2"
                    i=`expr length $1 + 1`
                    shift 1
                fi;;
            l )
                if [ $i -ne $length ] || [ $# -eq 1 ]; then
                    echo "Argument -l has no parameters after it"
                else
                    linkeropts="$2"
                    i=`expr length $1 + 1`
                    shift 1
                fi;;
            g )
                generate=1;;
            esac
            i=`expr $i + 1`
        done
    fi
    shift 1
done

# Ulozit optiony do souboru ./makerfile
> ./makerfile
echo "This file was automatically generated by maker" >> ./makerfile
echo $linkeropts >> ./makerfile
echo $cflags >> ./makerfile
echo $mainname >> ./makerfile

if [ $generate -eq 0 ]; then
    exit
fi

ls -1 ./*.c ./*.cpp 2> /dev/null | sed "s/\.\///g" > maker.files
> makefile
cat maker.files | grep ".cpp\$" - > /dev/null
if [ $? -ne 0 ]; then
    compiler="gcc"
else
    compiler="g++"
fi

echo "Processing dependencies for source files..."

i=1
length=`wc -l maker.files | cut -d " " -f 1`
while [ "$i" -le "$length" ]; do
    file=`cat maker.files | sed -n "${i}p"`
    echo "$file" > maker.deps
    #Generate dependencies
    searchforincludes;
    #Create target
    echo >> makefile
    echo -n "$file" | sed "s/\.cpp\$/\.o/;s/\.c\$/\.o/" >> makefile
    echo -n ": " >> makefile
    cat maker.deps | xargs -i echo -n "{} " >> makefile
    echo >> makefile
    echo "	\${COMPILER} \${CFLAGS} -c $file " >> makefile
    i=`expr $i + 1`
done

echo "Done."
echo "Building object files..."

> maker.tmp
> maker.vars
echo "#This makefile was generated by maker 0.1" >> maker.vars
echo "COMPILER=$compiler" >> maker.vars
echo "CFLAGS=$cflags" >> maker.vars
echo "LIBS=$linkeropts" >> maker.vars
echo >> maker.vars
echo -n "all: " >> maker.tmp
cat maker.files | sed "s/\.cpp\$/\.o/;s/\.c\$/\.o/" | xargs -i echo -n "{} " >> maker.tmp
echo >> maker.tmp
echo >> maker.tmp
echo "clean: " >> maker.tmp
echo -n "	rm -f " >> maker.tmp
cat maker.files | sed "s/\.cpp\$/\.o/;s/\.c\$/\.o/" | xargs -i echo -n "{} " >> maker.tmp
echo >> maker.tmp
cp -f makefile maker.tmp2
cat maker.vars maker.tmp maker.tmp2 > makefile

make clean > /dev/null
make all

echo "Done."
echo "Resolving undefined references for the targets..."

ls -1 *.o 2> /dev/null | xargs -i nm -o {} 2> /dev/null | sed "s/:/ /" | tr -s " " " " | awk "(\$2 != \"U\") {print \$1, \$4}" > defined.symbols

> maker.tmp
> maker.targets
i=1
while [ "$i" -le "$length" ]; do
    file=`cat maker.files | sed -n "${i}p"`
    echo "$file" | searchformain
    if [ $? -eq 0 ]; then
        echo "$file" | sed "s/\.cpp\$/\.o/;s/\.c\$/\.o/" > maker.objects
        > maker.undefined
        searchforreferences;
        
        target=`echo "$file" | sed "s/\.cpp\$//;s/\.c\$//"`
        if [ "$target" = "main" ] && [ "$mainname" != "" ]; then
            target="$mainname"
        fi
        echo "$target" >> maker.targets
        echo -n "$target:" >> maker.tmp
        cat maker.objects | xargs -i echo -n " {}" >> maker.tmp
        echo >> maker.tmp
        echo -n "	\${COMPILER} -o $target" >> maker.tmp
        cat maker.objects | xargs -i echo -n " {}" >> maker.tmp
        echo " \${LIBS}" >> maker.tmp
        echo >> maker.tmp
    fi
    i=`expr $i + 1`
done

echo "Done."
echo "Generating makefile..."

> maker.tmp3
echo -n "all:" >> maker.tmp3
cat maker.targets | xargs -i echo -n " {}" >> maker.tmp3
echo >> maker.tmp3
echo >> maker.tmp3
echo "clean:" >> maker.tmp3
echo -n "	rm -f ./*.o " >> maker.tmp3
cat maker.targets | xargs -i echo -n " {}" >> maker.tmp3
echo >> maker.tmp3
echo >> maker.tmp3

cat maker.vars maker.tmp3 maker.tmp maker.tmp2 > makefile

echo "Done."
echo "Compiling targets..."

make all

echo "Done."